﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml;

namespace editor.cfg
{
    public class Defines
    {

        private readonly Dictionary<string, object> types = new Dictionary<string, object>();

        private readonly Dictionary<string, Config> configs = new Dictionary<string, Config>();

        private readonly Dictionary<string, Bean> beans = new Dictionary<string, Bean>();

        private readonly Dictionary<string, ENUM> enums = new Dictionary<string, ENUM>();

        public Dictionary<string, Bean> Beans { get { return beans; } }
        public Dictionary<string, ENUM> Enums { get { return enums; } }
        public Dictionary<string, Config> Configs { get { return configs; } }

        public List<Config> EditorConfigs
        {
            get
            {
                var list = configs.Values.Where(c => c.IsEditorConfig).ToList();
                list.Sort((c1, c2) => c2.ShowOrder - c1.ShowOrder);
                return list;
            }
        }

        public bool Exist(string type)
        {
            return types.ContainsKey(type);
        }

        public void Add(ENUM e)
        {
            if (types.ContainsKey(e.FullName))
                Err($"enum {e.FullName} duplicate");
            types.Add(e.FullName, e);
            enums.Add(e.FullName, e);
        }

        public void Add(Bean e)
        {
            if (types.ContainsKey(e.FullName))
                Err($"bean {e.FullName} duplicate");
            types.Add(e.FullName, e);
            beans.Add(e.FullName, e);
        }

        public void Add(Config e)
        {
            if (configs.ContainsKey(e.SimpleName))
                Err($"config {e.SimpleName} duplicate");
            configs.Add(e.SimpleName, e);
        }

        public void Load(string cfgXmlPath)
        {
            LoadDefine(cfgXmlPath);
            Resolve();
        }

        public string CurLoadingXml { get; set; }

        private void Err(string msg)
        {
            throw new Exception(msg);
        }

        private void LoadDefine(string xmlFile)
        {
            CurLoadingXml = xmlFile;
            Log.Debug("load xml:{0}", xmlFile);
            var doc = new XmlDocument();
            doc.Load(xmlFile);
            var root = doc.DocumentElement;
            var module = root.Attributes["name"]?.Value;
            if (string.IsNullOrEmpty(module))
                Err($"module not define attr namespace");
            var directory = Path.GetDirectoryName(xmlFile);
            LoadDefine(root, module, directory);
        }

        private void LoadDefine(XmlNode node, string module, string dir)
        {
            Log.Debug("module:{0} directory:{1}", module, dir);
            foreach (XmlNode ele in node.ChildNodes)
            {
                switch (ele.Name)
                {
                    case "const": break;
                    case "group":
                        {
                            Log.Debug("group:{0}", ele.Attributes["name"].Value);
                            break;
                        }
                    case "include":
                        {
                            var input = ele.Attributes["name"].Value;
                            var oldLoadingXml = CurLoadingXml;
                            foreach (string file in input.Split(':'))
                            {
                                LoadDefine(Path.Combine(dir, file));
                            }
                            CurLoadingXml = oldLoadingXml;
                            break;
                        }
                    case "enum":
                        {
                            Log.Debug("load enum:{0}", ele.Attributes["name"]?.Value);
                            Add(new ENUM(this, module, ele as XmlElement));
                            break;
                        }
                    case "bean":
                        {
                            Log.Debug("load bean:{0}", ele.Attributes["name"]?.Value);
                            Add(new Bean(this, module, ele as XmlElement));
                            break;
                        }
                    case "config":
                        {
                            Log.Debug("load config:{0}", ele.Attributes["name"]?.Value);
                            var bean = new Bean(this, module, ele as XmlElement);
                            Add(bean);
                            var config = new Config(this, bean, dir, ele as XmlElement);
                            Add(config);
                            break;
                        }
                    case "module":
                        {
                            var submodule = ele.Attributes["name"]?.Value;
                            if (string.IsNullOrEmpty(submodule))
                                Err("sub module name missing");
                            LoadDefine(ele, module + "." + submodule, dir);
                            break;
                        }
                    default:
                        {
                            if (!ele.Name.StartsWith("#"))
                                Err($"unknown tag:{ele.Name}");
                            break;
                        }
                }
            }
        }

        public void Resolve()
        {
            foreach (Bean b in beans.Values)
                b.Resolve();

            foreach (Config c in configs.Values)
                c.ResolveDefine();
        }
    }



    public class Configs
    {
        public Defines Defines { get; private set; }

        public bool Load(string cfgXmlPath)
        {
            try
            {
                this.Defines = new Defines();
                Defines.Load(cfgXmlPath);
            }
            catch (Exception e)
            {
                MessageBox.Show($"{Defines.CurLoadingXml} 加载失败.\n {e.Message} \n {e.StackTrace}");
                return false;
            }

            return LoadData();
        }


        public bool LoadData()
        {
            foreach (Config c in Defines.Configs.Values)
            {
                try
                {
                    c.Load();
                }
                catch (Exception e)
                {
                    MessageBox.Show($"配置表{c.ValueType.BeanType.FullName} 加载失败.\n {e.Message} \n {e.StackTrace}");
                    return false;
                }
            }
            ResolveData();
            return true;
        }

        public void ResolveData()
        {
            foreach (Config c in Defines.Configs.Values)
            {
                c.ResolveData();
            }
        }
    }
}
